package com.jvm.classfile.model;

import com.jvm.Utils;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.Objects;

/**
 * @program: diy_simple_jvm
 * @description: 方法信息
 * @author: CaoYong
 * @create: 2021-07-08 14:34
 **/
@Data
@AllArgsConstructor
public class MethodInfo {

    U2 accessFlags; //访问标志
    U2 nameIndex; //方法名索引
    U2 descriptorIndex; //方法描述索引
    U2 attributesCount; //属性数
    AttributeInfo[] attributes; //属性

    public Code getCode(ClassFile classFile) {
        Code code = null;
        final CpInfo[] constantsPool = classFile.constantPool;
        for(AttributeInfo attribute: this.attributes) {
            final String attrName = Utils.getUtf8(constantsPool, attribute.attributeNameIndex.val);
            if (Objects.equals("Code", attrName)) {
                final byte[] bytes = attribute.info;
                try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                     final DataInputStream dis = new DataInputStream(bis)
                ) {
                    final int maxStack = dis.readUnsignedShort();
                    final int maxLocals = dis.readUnsignedShort();
                    final int codeLength = dis.readInt();
                    byte[] codeBytes = new byte[codeLength];
                    dis.read(codeBytes);

                    final int exceptionTableLength = dis.readUnsignedShort();
                    final ExceptionTableItem[] exceptionTable = new ExceptionTableItem[exceptionTableLength];
                    if (exceptionTableLength > 0) {
                        for(int j = 0; j < exceptionTableLength; j++) {
                            exceptionTable[j] = new ExceptionTableItem(dis.readUnsignedShort(), dis.readUnsignedShort(),dis.readUnsignedShort(), dis.readUnsignedShort());
                        }
                    }

                    final int attributeCount = dis.readUnsignedShort();
                    AttributeInfo[] attributeInfos = new AttributeInfo[attributeCount];

                    for (int i = 0; i < attributeInfos.length; i++) {
                        int attributeNameIndex = dis.readUnsignedShort();
                        int attributeLength = dis.readInt();
                        byte[] info = new byte[attributeLength];
                        dis.read(info);
                        attributeInfos[i] = new AttributeInfo(new U2(attributeNameIndex), new U4(attributeLength), info);
                    }

                    code = new Code(maxStack, maxLocals, codeBytes, exceptionTable, attributeInfos);

                } catch (Exception e) {
                    e.printStackTrace();
                    throw new IllegalStateException("unknown exception");
                }
            }
        }
        return code;
    }
}
